from django.http import JsonResponse
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.views.decorators.csrf import csrf_exempt
from ..models.audio import Audio
import os
from django.conf import settings
import soundfile as sf
from kokoro_onnx import Kokoro
import uuid
from datetime import datetime
from kokoro import KModel, KPipeline
from pathlib import Path
import numpy as np
import soundfile as sf
import torch
import tqdm
import os
import json
from ..utils.voice_types import get_all_voice_types, get_voice_types_by_engine, get_voice_types_by_lang_code, get_voice_by_id
from decouple import config
from django.utils import timezone
import asyncio
import threading
from django.db import connection
from django.db.utils import OperationalError
import time
import websocket
from api.utils.response import success_response, error_response

# 设置环境变量指向本地模型目录
os.environ['KOKORO_MODEL_DIR'] = str(Path(__file__).parent)
REPO_ID = 'hexgrad/Kokoro-82M-v1.1-zh'
SAMPLE_RATE = 24000
# How much silence to insert between paragraphs: 5000 is about 0.2 seconds
N_ZEROS = 5000

# Whether to join sentences in paragraphs 1 and 3
JOIN_SENTENCES = False

# VOICE = 'zf_002' if True else 'zm_010'

device = 'cuda' if torch.cuda.is_available() else 'cpu'
# 直接使用本地模型文件 中文模型文件
model = KModel(repo_id=REPO_ID,config=f"{settings.BASE_DIR}/tts_models/config.json",       # <--- 提供本地 config.json 路径
        model=f"{settings.BASE_DIR}/tts_models/kokoro-v1_1-zh.pth"  # <--- 提供本地 .pth 文件路径
).to(device).eval()

def retry_db_operation(func, max_retries=3, delay=1):
    """
    数据库操作重试装饰器
    """
    def wrapper(*args, **kwargs):
        for attempt in range(max_retries):
            try:
                # 确保数据库连接是活跃的
                if not connection.is_usable():
                    connection.close()
                    connection.connect()
                return func(*args, **kwargs)
            except (OperationalError, Exception) as e:
                if attempt == max_retries - 1:
                    raise
                print(f"数据库操作失败，正在重试 ({attempt + 1}/{max_retries})... 错误: {str(e)}")
                time.sleep(delay)
                # 重新建立数据库连接
                connection.close()
                connection.connect()
        return func(*args, **kwargs)
    return wrapper

@retry_db_operation
def update_audio_status(audio_id, status, url=None):
    """
    更新音频状态
    """
    audio = Audio.objects.get(id=audio_id)
    audio.status = status
    if url:
        audio.url = url
    audio.save()

def send_progress_message(audio_id, message):
    """
    发送进度消息到WebSocket服务器
    """
    try:
        ws = websocket.WebSocket()
        ws.connect(f"wss://crmeb.tietaozhe.com/ws")
        ws.send(json.dumps(message))
        ws.close()
    except Exception as e:
        print(f"发送WebSocket消息失败: {str(e)}")

# 异步处理函数
def process_audio_async(audio_id, text, voice, speed, lang_code, file_name):
    """
    异步处理音频
    """
    try:
        # 获取音频记录
        audio = Audio.objects.get(id=audio_id)
        
        # 更新状态为处理中
        update_audio_status(audio_id, 'processing')
        
        # 发送开始处理消息
        # send_progress_message(audio_id, {
        #     'type': 'progress',
        #     'status': 'processing',
        #     'message': '开始处理音频',
        #     'audio_id': audio_id,
        #     'to_uid': audio.user.id
        # })
        
        if lang_code == 'z':
            # 中文处理
            # 将文本分段处理，避免服务器卡顿
            paragraphs = text.split('\n')
            
            # 如果段落太长，进一步分割成更小的句子
            processed_paragraphs = []
            for paragraph in paragraphs:
                if len(paragraph) > 100:  # 如果段落超过100个字符
                    # 按标点符号分割
                    sentences = []
                    current_sentence = ""
                    for char in paragraph:
                        current_sentence += char
                        if char in '。！？.!?，,':  # 在标点符号处分割
                            if len(current_sentence) > 0:
                                sentences.append(current_sentence)
                            current_sentence = ""
                    if len(current_sentence) > 0:
                        sentences.append(current_sentence)
                    processed_paragraphs.append(sentences)
                else:
                    processed_paragraphs.append([paragraph])
            
            texts = processed_paragraphs

            if JOIN_SENTENCES:
                for i in (1, 3):
                    if i < len(texts):
                        texts[i] = [''.join(texts[i])]

            # 简化英文处理，直接返回原文
            def en_callable(text):
                return text

            # 创建中文处理管道
            zh_pipeline = KPipeline(lang_code='z', repo_id=REPO_ID, model=model, en_callable=en_callable)
            
            # 修改临时文件保存路径为 media/audio/temp 目录
            temp_dir = os.path.join(settings.MEDIA_ROOT, 'audio', 'temp')
            # 确保临时目录存在
            os.makedirs(temp_dir, exist_ok=True)
            wavs = []
            temp_files = []  # 存储临时文件路径
            
            # 使用进度条显示处理进度
            total_paragraphs = len(texts)
            
            for paragraph_idx, paragraph in enumerate(tqdm.tqdm(texts, desc="处理段落")):
                
                
                total_sentences = len(paragraph)
                for sentence_idx, sentence in enumerate(tqdm.tqdm(paragraph, desc=f"处理段落 {paragraph_idx+1} 中的句子", leave=False)):
                    send_progress_message(audio_id, {
                        'type': 'progress',
                        'to_uid': audio.user.id,
                        'audio_id': audio_id,
                        'status': 'processing',
                        'total_paragraphs': total_paragraphs,
                        'current_paragraph': paragraph_idx,
                        'total_sentences': total_sentences,
                        'current_sentence': sentence_idx,
                        'message': f'正在处理第 {paragraph_idx + 1}/{total_paragraphs} 个段落的第 {sentence_idx + 1}/{total_sentences} 个句子'
                    })
                    
                    # 跳过空句子
                    if not sentence.strip():
                        continue
                    
                    try:
                        # 使用本地声音类型文件
                        voice_path = f"{settings.BASE_DIR}/tts_models/voices/{voice}.pt"
                        if not os.path.exists(voice_path):
                            send_progress_message(audio_id, {
                                'to_uid': audio.user.id,
                                'type': 'warning',
                                'audio_id': audio_id,
                                'message': f'警告: 语音文件 {voice_path} 不存在，使用默认语音'
                            })
                            voice_path = None
                        
                        generator = zh_pipeline(sentence, voice=voice_path, speed=speed)
                        # 使用临时目录保存临时文件
                        f = os.path.join(temp_dir, f'zh{uuid.uuid4()}.mp3')
                        temp_files.append(f)  # 将临时文件路径添加到数组中
                        
                        try:
                            result = next(generator)
                            wav = result.audio
                            sf.write(f, wav, SAMPLE_RATE)
                            if sentence_idx == 0 and wavs and N_ZEROS > 0:
                                wav = np.concatenate([np.zeros(N_ZEROS), wav])
                            wavs.append(wav)
                            send_progress_message(audio_id, {
                                'to_uid': audio.user.id,
                                'type': 'progress',
                                'audio_id': audio_id,
                                'status': 'processing',
                                'total_paragraphs': total_paragraphs,
                                'current_paragraph': paragraph_idx,
                                'total_sentences': total_sentences,
                                'current_sentence': sentence_idx,
                                'message': f'成功处理第 {paragraph_idx + 1}/{total_paragraphs} 个段落的第 {sentence_idx + 1}/{total_sentences} 个句子'
                            })
                        except Exception as e:
                            send_progress_message(audio_id, {
                                'to_uid': audio.user.id,
                                'type': 'error',
                                'audio_id': audio_id,
                                'message': f"处理句子 '{sentence}' 时出错: {str(e)}"
                            })
                            # 继续处理下一个句子
                            continue
                    except Exception as e:
                        send_progress_message(audio_id, {
                            'to_uid': audio.user.id,
                            'type': 'error',
                            'audio_id': audio_id,
                            'message': f"创建生成器时出错: {str(e)}"
                        })
                        # 继续处理下一个句子
                        continue
                
               
                send_progress_message(audio_id, {
                        'type': 'progress',
                        'to_uid': audio.user.id,
                        'audio_id': audio_id,
                        'status': 'processing',
                        'total_paragraphs': total_paragraphs,
                        'current_paragraph': paragraph_idx + 1,
                        'total_sentences': total_sentences,
                        'current_sentence': total_sentences,
                        'message': f'完成第 {paragraph_idx + 1}/{total_paragraphs} 个段落的处理'
                })
            
            send_progress_message(audio_id, {
                'to_uid': audio.user.id,
                'type': 'progress',
                'audio_id': audio_id,
                'status': 'adding',
                'message': '所有段落处理完成，开始合并音频...'
            })
            
            # 检查是否有生成的音频
            if not wavs:
                raise RuntimeError("没有生成任何音频，请检查输入文本和模型")
            
            # 本地存储
            file_path = os.path.join(settings.LOCAL_AUDIO_ROOT, file_name)
            # 确保目标文件夹存在
            os.makedirs(os.path.dirname(file_path), exist_ok=True)
            sf.write(file_path, np.concatenate(wavs), SAMPLE_RATE)
            
            # 删除所有临时文件
            for temp_file in temp_files:
                if os.path.exists(temp_file):
                    os.remove(temp_file)
                    
            # 所有处理完成后，更新数据库状态
            try:
                domain_name = config('DOMAIN_NAME', default='192.168.220.131')
                update_audio_status(audio_id, 'completed', f"/media/audio/{file_name}")
                send_progress_message(audio_id, {
                    'to_uid': audio.user.id,
                    'type': 'progress',
                    'audio_id': audio_id,
                    'status': 'completed',
                    'message': f'音频处理完成，文件已保存: {file_name}',
                    'url': f"{domain_name}/media/audio/{file_name}"
                })
            except Exception as e:
                send_progress_message(audio_id, {
                    'to_uid': audio.user.id,
                    'type': 'error',
                    'audio_id': audio_id,
                    'message': f"更新数据库状态时出错: {str(e)}"
                })
                raise
        else:
            # 英文处理
            # 初始化 Kokoro
            kokoro2 = Kokoro(f"{settings.BASE_DIR}/tts_models/kokoro-v1.0.onnx", f"{settings.BASE_DIR}/tts_models/voices-v1.0.bin")
            
            # 分段处理英文文本
            if len(text) > 100:
                # 按句子分割文本
                sentences = text.split('. ')
                segments = []
                current_segment = ''
                for sentence in sentences:
                    if len(current_segment) + len(sentence) + 1 <= 100:
                        current_segment += sentence + '. '
                    else:
                        segments.append(current_segment)
                        current_segment = sentence + '. '
                if current_segment:
                    segments.append(current_segment)
            else:
                segments = [text]

            # 发送进度消息
            total_segments = len(segments)
            # send_progress_message(audio_id, {
            #     'to_uid': audio.user.id,
            #     'type': 'progress',
            #     'audio_id': audio_id,
            #     'status': 'processing',
            #     'total_segments': total_segments,
            #     'current_segment': 0,
            #     'message': f'开始处理英文音频，共 {total_segments} 个段落'
            # })
            # send_progress_message(audio_id, {
            #     'type': 'progress',
            #     'to_uid': audio.user.id,
            #     'audio_id': audio_id,
            #     'status': 'processing',
            #     'total_paragraphs': total_segments,
            #     'current_paragraph': 0,
            #     'total_sentences': 0,
            #     'current_sentence': 0,
            #     'message': f'开始处理英文音频，共 {total_segments} 个段落'
            # })

            # 合成每个分段并拼接
            all_samples = []
            for segment_idx, segment in enumerate(segments):
                # send_progress_message(audio_id, {
                #     'to_uid': audio.user.id,
                #     'type': 'progress',
                #     'audio_id': audio_id,
                #     'status': 'processing',
                #     'total_segments': total_segments,
                #     'current_segment': segment_idx + 1,
                #     'message': f'正在处理第 {segment_idx + 1}/{total_segments} 个段落'
                # })
                send_progress_message(audio_id, {
                    'type': 'progress',
                    'to_uid': audio.user.id,
                    'audio_id': audio_id,
                    'status': 'processing',
                    'total_paragraphs': total_segments,
                    'current_paragraph': segment_idx,
                    'total_sentences': 0,
                    'current_sentence': 0,
                    'message': f'正在处理第 {segment_idx + 1}/{total_segments} 个段落'
                })
                
                if segment:  # 确保 segment 不为空
                    samples, sample_rate = kokoro2.create(
                        text=segment,
                        voice=voice,
                        speed=speed,
                        lang="en-us"
                    )
                    if samples is not None and len(samples) > 0:  # 确保 samples 不为空
                        all_samples.extend(samples)
            send_progress_message(audio_id, {
                'type': 'progress',
                'to_uid': audio.user.id,
                'audio_id': audio_id,
                'status': 'processing',
                'total_paragraphs': total_segments,
                'current_paragraph': total_segments,
                'total_sentences': 0,
                'current_sentence': 0,
                'message': f'所有段落处理完成，开始合并音频...'
            })
            # 确保至少有一个音频段
            if not all_samples:
                raise ValueError("没有生成任何音频，请检查输入文本和模型")

            samples = all_samples

            # 本地存储
            file_path = os.path.join(settings.LOCAL_AUDIO_ROOT, file_name)
            # 确保目标文件夹存在
            os.makedirs(os.path.dirname(file_path), exist_ok=True)
            sf.write(file_path, samples, sample_rate)
            
            # 更新状态为完成
            update_audio_status(audio_id, 'completed', f"/media/audio/{file_name}")
            domain_name = config('DOMAIN_NAME', default='192.168.220.131')
            send_progress_message(audio_id, {
                'to_uid': audio.user.id,
                'audio_id': audio_id,
                'type': 'progress',
                'status': 'completed',
                'message': f'英文音频处理完成',
                'url': f"{domain_name}/media/audio/{file_name}"
            })
        
    except Exception as e:
        # 更新音频记录状态为失败
        try:
            update_audio_status(audio_id, 'failed')
            send_progress_message(audio_id, {
                'to_uid': audio.user.id,
                'audio_id': audio_id,
                'type': 'error',
                'message': f'音频处理失败: {str(e)}'
            })
        except:
            pass
        print(f"处理音频时出错: {str(e)}")

@csrf_exempt
def audio_list(request):
    """
    获取音频列表，支持分页
    参数：
    - page: 页码，默认为1
    - size: 每页数量，默认为10
    """
    if request.method == 'GET':
        try:
            # 获取分页参数
            page = request.GET.get('page', 1)
            size = request.GET.get('size', 10)
            
            # 尝试将参数转换为整数
            try:
                page = int(page)
                size = int(size)
            except ValueError:
                return error_response(message='分页参数必须是整数',code=400)
            
            # 获取用户信息
            token = request.headers.get('Authorization')
            if not token:
                return error_response(message='未提供认证令牌',code=401)
            
            # 获取用户信息
            from ..logic.wechat import WechatLogic
            wechat_logic = WechatLogic()
            user = wechat_logic.get_user_by_token(token)
            if not user:
                return error_response(message='未登录或登录已过期',code=401)
            
            # 获取当前用户的音频记录
            audio_records = Audio.objects.filter(user=user).order_by('-created_at')
            
            # 创建分页器
            paginator = Paginator(audio_records, size)
            
            try:
                # 获取当前页的数据
                current_page = paginator.page(page)
            except PageNotAnInteger:
                # 如果页码不是整数，返回第一页
                current_page = paginator.page(1)
            except EmptyPage:
                # 如果页码超出范围，返回最后一页
                current_page = paginator.page(paginator.num_pages)
            domain_name = config('DOMAIN_NAME', default='192.168.220.131')
            # 准备返回数据
            audio_list = []
            for audio in current_page:
                # 根据 status 字段的值返回对应的中文状态文本
                status_text = {
                    'pending': '等待处理',
                    'processing': '处理中',
                    'completed': '已完成',
                    'failed': '处理失败'
                }.get(audio.status, '未知状态')

                audio_list.append({
                    'id': audio.id,
                    'title': audio.title,
                    'text': audio.text,
                    'voice': audio.voice,
                    'rate': audio.rate,
                    'volume': audio.volume,
                    'style': audio.style,
                    'style_degree': audio.style_degree,
                    'status': audio.status,
                    'status_text': status_text,
                    'file_name': audio.file_name,
                    'url': f"{domain_name}{audio.url}",
                    'created_at': timezone.localtime(audio.created_at).strftime('%Y-%m-%d %H:%M:%S')
                })
            
            # 返回分页数据
            return success_response(
                message='音频记录获取成功',
                data={
                    'list': audio_list,
                    'pagination': {
                        'total': paginator.count,
                        'page': current_page.number,
                        'size': size,
                        'pages': paginator.num_pages,
                        'has_next': current_page.has_next(),
                        'has_previous': current_page.has_previous()
                    }
                }
            )
        except Exception as e:
            return error_response(message=f'获取音频记录失败：{str(e)}',code=500)
    else:
        return error_response(message='仅支持GET请求',code=405)

@csrf_exempt
def audio_delete(request, audio_id):
    """
    删除音频记录
    参数：
    - audio_id: 音频记录ID
    """
    if request.method == 'DELETE':
        try:
            # 查找音频记录
            try:
                audio = Audio.objects.get(id=audio_id)
            except Audio.DoesNotExist:
                return error_response(message=f'未找到ID为{audio_id}的音频记录',code=404)
            
            # 删除音频文件
            file_path = None
            if settings.AUDIO_STORAGE_TYPE == 'local':
                # 本地存储
                file_path = os.path.join(settings.LOCAL_AUDIO_ROOT, audio.file_name)
                if os.path.exists(file_path):
                    os.remove(file_path)
            else:
                # OSS存储，这里需要实现OSS删除逻辑
                # 由于OSS删除需要额外的配置，这里暂时不实现
                pass
            
            # 删除数据库记录
            audio.delete()
            
            return success_response(message='音频记录删除成功')
        except Exception as e:
            return error_response(message=f'删除音频记录失败：{str(e)}',code=500)
    else:
        return error_response(message='仅支持DELETE请求',code=405)

@csrf_exempt
def kokoro_synthesize(request):
    """
    使用 Kokoro 进行语音合成
    参数：
    - text: 要合成的文本
    - voice: 语音ID（可选，默认使用 zf_078）
    - speed: 语速（可选，默认 1.0）
    """
    if request.method == 'POST':
        try:
           
            data = json.loads(request.body)
            # 获取请求参数
            text = data.get('text')
            voice = data.get('voice', 'zf_002')
            speed = float(data.get('speed', 1.0))
            lang_code = data.get('lang_code', 'z')
            volume = data.get('volume', 1.0)
            
            # 获取用户信息
            token = request.headers.get('Authorization')
            if not token:
                return error_response(message='未提供token',code=401)
            
            # 获取用户信息
            from ..logic.wechat import WechatLogic
            wechat_logic = WechatLogic()
            user = wechat_logic.get_user_by_token(token)
            if not user:
                return error_response(message='未登录或登录已过期',code=401)
            
            # 检查用户是否有正在处理的音频
            processing_audio = Audio.objects.filter(
                user=user,
                status__in=['pending', 'processing']
            ).first()

            if processing_audio:
                return error_response(
                    message='您有正在处理的音频，请等待处理完成后再试或在【我的-配音历史】删除记录',
                    code=400,
                    data={
                        'id': processing_audio.id,
                        'status': processing_audio.status,
                        'status_text': {
                            'pending': '等待处理',
                            'processing': '处理中',
                            'completed': '已完成',
                            'failed': '处理失败'
                        }.get(processing_audio.status, '未知状态')
                    }
                )
            
            # 检查用户是否是VIP会员
            from ..models.vip import VipMember
            try:
                vip_member = VipMember.objects.get(user=user)
                is_vip = vip_member.is_active and vip_member.check_vip_status()
            except VipMember.DoesNotExist:
                is_vip = False
            
            # 检查文本长度限制
            text_length = len(text)
            if not is_vip and text_length > 500:
                return error_response(message='免费用户单次合成字符数不能超过500字',code=403)
            
            # 检查每日使用次数限制
            from ..models.audio import AudioUsage
            from django.utils import timezone
            today = timezone.now().date()
            
            # 获取或创建用户今日使用记录
            usage, created = AudioUsage.objects.get_or_create(
                user=user,
                date=today,
                defaults={'total_chars': 0, 'total_count': 0}
            )
            
            # 检查免费用户每日使用次数限制
            if not is_vip and usage.total_count >= 10:
                return error_response(message='免费用户每日合成次数不能超过10次',code=403)
            
            if not text:
                return error_response(message='文本内容不能为空',code=400)

            # 生成文件名
            file_name = f"tts/{datetime.now().strftime('%Y%m%d')}/{uuid.uuid4()}.mp3"
            
            # 创建数据库记录
            audio = Audio.objects.create(
                title=f"语音_{datetime.now().strftime('%Y%m%d%H%M%S')}",
                text=text,
                voice=voice,
                rate=speed,
                file_name=file_name,
                url="",  # 初始为空，异步任务完成后更新
                user=user,
                status='pending',  # 添加状态字段，表示正在处理中
                volume=volume
            )
            
            # 更新用户使用记录
            usage.total_chars += text_length
            usage.total_count += 1
            usage.save()
            
            # 默认使用异步任务处理
            if text_length > 0:
                # 使用线程进行异步处理
                thread = threading.Thread(
                    target=process_audio_async,
                    args=(audio.id, text, voice, speed, lang_code, file_name)
                )
                thread.daemon = True  # 设置为守护线程，主线程结束时自动结束
                thread.start()
                
                # 返回任务ID和状态
                domain_name = config('DOMAIN_NAME', default='192.168.220.131')
                return success_response(
                    message='语音正在异步生成中，请稍后在配音历史查询结果',
                    data={
                        'id': audio.id,
                        'title': audio.title,
                        'text': audio.text,
                        'voice': audio.voice,
                        'rate': audio.rate,
                        'file_name': audio.file_name,
                        'status': 'pending',
                        'usage': {
                            'total_chars': usage.total_chars,
                            'total_count': usage.total_count,
                            'is_vip': is_vip,
                            'daily_limit': 10 if not is_vip else None,
                            'chars_limit': 500 if not is_vip else 5000
                        }
                    }
                )
            else:
                # 文本长度不超过100个字，直接处理
                if(lang_code == 'z'):
                    # 直接使用本地模型文件
                    # model = KModel(repo_id=REPO_ID).to(device).eval()
                    
                    # 将文本分段处理，避免服务器卡顿
                    # 按段落分割文本
                    paragraphs = text.split('\n')
                    
                    # 如果段落太长，进一步分割成更小的句子
                    processed_paragraphs = []
                    for paragraph in paragraphs:
                        if len(paragraph) > 100:  # 如果段落超过100个字符
                            # 按标点符号分割
                            sentences = []
                            current_sentence = ""
                            for char in paragraph:
                                current_sentence += char
                                if char in '。！？.!?，,':  # 在标点符号处分割
                                    if len(current_sentence) > 0:
                                        sentences.append(current_sentence)
                                    current_sentence = ""
                            if len(current_sentence) > 0:
                                sentences.append(current_sentence)
                            processed_paragraphs.append(sentences)
                        else:
                            processed_paragraphs.append([paragraph])
                    
                    texts = processed_paragraphs

                    if JOIN_SENTENCES:
                        for i in (1, 3):
                            if i < len(texts):
                                texts[i] = [''.join(texts[i])]

                    # 简化英文处理，直接返回原文
                    def en_callable(text):
                        return text

                    # HACK: Mitigate rushing caused by lack of training data beyond ~100 tokens
                    # Simple piecewise linear fn that decreases speed as len_ps increases
                    # def speed_callable(len_ps):
                    #     speed = 0.8
                    #     if len_ps <= 83:
                    #         speed = 1
                    #     elif len_ps < 183:
                    #         speed = 1 - (len_ps - 83) / 500
                    #     return speed * 1.1

                    zh_pipeline = KPipeline(lang_code='z', repo_id=REPO_ID, model=model, en_callable=en_callable)
                    # 修改临时文件保存路径为 media/audio/temp 目录
                    temp_dir = os.path.join(settings.MEDIA_ROOT, 'audio', 'temp')
                    # 确保临时目录存在
                    os.makedirs(temp_dir, exist_ok=True)
                    wavs = []
                    temp_files = []  # 存储临时文件路径
                    
                    # 使用进度条显示处理进度
                    total_paragraphs = len(texts)
                    print(f"开始处理音频，共 {total_paragraphs} 个段落")
                    
                    for paragraph_idx, paragraph in enumerate(tqdm.tqdm(texts, desc="处理段落")):
                        print(f"正在处理第 {paragraph_idx + 1}/{total_paragraphs} 个段落")
                        total_sentences = len(paragraph)
                        for sentence_idx, sentence in enumerate(tqdm.tqdm(paragraph, desc=f"处理段落 {paragraph_idx+1} 中的句子", leave=False)):
                            print(f"正在处理第 {paragraph_idx + 1}/{total_paragraphs} 个段落的第 {sentence_idx + 1}/{total_sentences} 个句子")
                            # 跳过空句子
                            if not sentence.strip():
                                continue
                            # 使用本地声音类型文件
                            generator = zh_pipeline(sentence, voice=f"{settings.BASE_DIR}/tts_models/voices/{voice}.pt", speed=speed)
                            # 使用临时目录保存临时文件
                            f = os.path.join(temp_dir, f'zh{uuid.uuid4()}.mp3')
                            temp_files.append(f)  # 将临时文件路径添加到数组中
                            result = next(generator)
                            wav = result.audio
                            sf.write(f, wav, SAMPLE_RATE)
                            if sentence_idx == 0 and wavs and N_ZEROS > 0:
                                wav = np.concatenate([np.zeros(N_ZEROS), wav])
                            wavs.append(wav)
                        
                        print(f"完成第 {paragraph_idx + 1}/{total_paragraphs} 个段落的处理")
                    
                    print("所有段落处理完成，开始合并音频...")
                    
                    # 本地存储
                    file_path = os.path.join(settings.LOCAL_AUDIO_ROOT, file_name)
                    # 确保目标文件夹存在
                    os.makedirs(os.path.dirname(file_path), exist_ok=True)
                    sf.write(file_path, np.concatenate(wavs), SAMPLE_RATE)
                    # 删除所有临时文件
                    for temp_file in temp_files:
                        if os.path.exists(temp_file):
                            os.remove(temp_file)
                else:
                    # 英文
                    # 初始化 Kokoro
                    kokoro2 = Kokoro(f"{settings.BASE_DIR}/tts_models/kokoro-v1.0.onnx", f"{settings.BASE_DIR}/tts_models/voices-v1.0.bin")
                    # kokoro2 = Kokoro("D:/web/python/text-to-audio-server/tts_models/kokoro-v1.0.onnx", "D:/web/python/text-to-audio-server/voices-v1.0.bin")
                    # 生成语音
                    # print(123243,kokoro2.get_voices())

                    # 分段处理英文文本
                    if len(text) > 100:
                        # 按句子分割文本
                        sentences = text.split('. ')
                        segments = []
                        current_segment = ''
                        for sentence in sentences:
                            if len(current_segment) + len(sentence) + 1 <= 100:
                                current_segment += sentence + '. '
                            else:
                                segments.append(current_segment)
                                current_segment = sentence + '. '
                        if current_segment:
                            segments.append(current_segment)
                    else:
                        segments = [text]

                    # 合成每个分段并拼接
                    all_samples = []
                    for segment in segments:
                        if segment:  # 确保 segment 不为空
                            samples, sample_rate = kokoro2.create(
                                text=segment,
                                voice=voice,
                                speed=speed,
                                lang="en-us"
                            )
                            if samples is not None and len(samples) > 0:  # 确保 samples 不为空
                                all_samples.extend(samples)

                    # 确保至少有一个音频段
                    if not all_samples:
                        raise ValueError("没有生成任何音频，请检查输入文本和模型")

                    samples = all_samples

                    # 本地存储
                    file_path = os.path.join(settings.LOCAL_AUDIO_ROOT, file_name)
                    # 确保目标文件夹存在
                    os.makedirs(os.path.dirname(file_path), exist_ok=True)
                    sf.write(file_path, samples, sample_rate)
                
                # 保存音频文件
                if settings.AUDIO_STORAGE_TYPE == 'local':
                    # 本地存储
                    # file_path = os.path.join(settings.LOCAL_AUDIO_ROOT, file_name)
                    # sf.write(file_path, samples, sample_rate)
                    url = f"/media/audio/{file_name}"
                else:
                    # OSS存储
                    file_path = f"/tmp/{file_name}"
                    sf.write(file_path, samples, sample_rate)
                    # 上传到OSS
                    with open(file_path, 'rb') as f:
                        settings.OSS_BUCKET.put_object(f"audio/{file_name}", f)
                    os.remove(file_path)
                    url = f"{settings.OSS_ENDPOINT}/audio/{file_name}"
                
                # 更新音频记录
                update_audio_status(audio.id, 'completed', url)
                
                domain_name = config('DOMAIN_NAME', default='192.168.220.131')
                return success_response(
                    message='语音合成成功',
                    data={
                        'id': audio.id,
                        'title': audio.title,
                        'text': audio.text,
                        'voice': audio.voice,
                        'rate': audio.rate,
                        'file_name': audio.file_name,
                        'url':  f"{domain_name}{url}",
                        'created_at': audio.created_at.strftime('%Y-%m-%d %H:%M:%S'),
                        'usage': {
                            'total_chars': usage.total_chars,
                            'total_count': usage.total_count,
                            'is_vip': is_vip,
                            'daily_limit': 10 if not is_vip else None,
                            'chars_limit': 500 if not is_vip else 5000
                        }
                    }
                )
            
        except Exception as e:
            return error_response(message=f'语音合成失败：{str(e)}',code=500)
    else:
        return error_response(message='仅支持POST请求',code=405)

@csrf_exempt
def voices(request):
    """
    获取可用的语音列表，按分类组织
    """
    if request.method == 'GET':
        try:
            # 获取查询参数
            engine = request.GET.get('engine')
            lang_code = request.GET.get('lang_code')
            
            # 根据参数获取语音列表
            if engine:
                voices = get_voice_types_by_engine(engine)
            elif lang_code:
                voices = get_voice_types_by_lang_code(lang_code)
            else:
                voices = get_all_voice_types()
                
            return success_response(
                message='获取语音列表成功',
                data=voices
            )
        except Exception as e:
            return error_response(message=f'获取语音列表失败：{str(e)}',code=500)
    else:
        return error_response(message='仅支持GET请求',code=405) 